package org.jprobe;

import java.io.File;
import java.util.HashSet;
import java.util.Set;

import org.jprobe.classpath.ClassPathReader;
import org.jprobe.utils.XmlUtils;
import org.w3c.dom.NodeList;

/**
 * Java project template
 *
 * @author Hussain Al-Mutawa
 * @version 1.0
 * @since 1.0
 **/
public class JavaProject implements JavaProjectInterface {

    /** the file location (directory) which is the project holding the packages */
    private transient final File PROJECT_DIR;
    /** the <code>[project]/bin</code> build directory of the project  */
    private transient File projectOutput;
    /** <code>[project]/jprobe/src</code> folder which contains generated classes in the runtime**/
    private transient final File JPROBE_SRC;
    /** <code>[project]/jprobe/bin</code> folder which contains compiled classes in the runtime**/
    private transient final File JPROBE_BIN;
    /** the <code>[project]/tmp</code> temporary directory of the project  */
    public transient final File PROJECT_TMP;
    /** the <code>[project]/tmp</code> temporary directory of the project  */
    public transient final File PROJECT_LIB;
    /** the list of libraries in the project **/
    private transient String[]libraries;
    /** a flag to indicate if the project class path has been loaded or not **/
    private transient boolean loaded;
    /** the list packages defined in the project, initially empty, until the project is loaded **/
	private transient String[] packages = {};
	/** the project name **/
	private transient final String name;
	/** list of classes in the project, default empty **/
	private transient String[]classes = {};

    /**
     * initializes new Reflection object and prepares its project
     * and build directory which is assumed to be <code>bin</code>
     * @param project the file location (directory) which is the project holding the packages
     */
    public JavaProject(final File project) {
            super();
            PROJECT_DIR=project;
            projectOutput = new File(this.PROJECT_DIR, "bin/");

            JPROBE_SRC = new File(this.PROJECT_DIR,"jprobe/");
            PROJECT_TMP = new File(this.PROJECT_DIR,"tmp/");
            PROJECT_LIB = new File(this.PROJECT_DIR,"lib/");
            JPROBE_BIN = PROJECT_TMP;

            JPROBE_SRC.mkdirs();
            loaded=false;
            this.name = project.getName();
    }

    @Override
    public File getLib() {
    	return PROJECT_LIB;
    }
    @Override
    public File getProjectDirectory() {
            return PROJECT_DIR;
    }
    @Override
    public File getBin() {
            return projectOutput;
    }
    @Override
    public File getJprobeSrcFolder() {
            return JPROBE_SRC;
    }
    @Override
    public File getJprobeBinFolder() {
            return JPROBE_BIN;
    }

    @Override
    public String[] getLibraries() throws Exception{
    	if(libraries==null){
	    	final NodeList nlist = XmlUtils.xpath(
	    			this.PROJECT_DIR.getAbsolutePath()+File.separator+".classpath",
	    			"classpath/classpathentry[@kind='lib']"
	    		);
	    	libraries=new String[nlist.getLength()];
	    	for(int i=0;i<libraries.length;i++){
	    		libraries[i]=nlist.item(i).getAttributes().getNamedItem("path").getNodeValue();
	    	}
    	}
    	return libraries;
    }
    @Override
    public void load() throws Exception{
    	if(loaded){
    		throw new IllegalStateException("This project has been already loaded");
    	}
    	this.packages=ClassPathReader.instance.addClassPathUrlToSystemClassLoader(getBin());
    	final Set<String>classesSet=new HashSet<String>();
    	for(String pkg:packages){
    		classesSet.addAll(ClassPathReader.instance.getClassPath().getClassesNamesOnly(pkg));
    	}
    	
    	this.classes = classesSet.toArray(new String[classesSet.size()]);
    	
    	for(final String lib:getLibraries()){
    		ClassPathReader.instance.addClassPathUrlToSystemClassLoader(this.PROJECT_DIR.getAbsolutePath()+File.separator+lib);
    	}
    	loaded=true;
    }
    @Override
    public void unload() throws Exception{
    	if(!loaded){
    		throw new IllegalStateException("This project has not been loaded");
    	}
    	ClassPathReader.instance.removeClassPathUrlFromSystemClassLoader(getBin().getCanonicalPath());
    	for(final String lib:getLibraries()){
    		ClassPathReader.instance.removeClassPathUrlFromSystemClassLoader(this.PROJECT_DIR.getAbsolutePath()+File.separator+lib);
    	}
    	loaded=false;
    }
    @Override
	public String[] getPackages() {
		return packages;
	}
    @Override
    public String toString(){
    	return "Java Project : " + this.getName();
    }
    @Override
    public String getName(){
    	return this.name;
    }
    @Override
	public boolean isLoaded() {
		return loaded;
	}
    
    @Override
    public String[] getClasses() {
    	return classes;
    }
}
